#ifndef _THRESHOLDING_PANEL_H_
#define _THRESHOLDING_PANEL_H_

#include "Source.h"
#include "VolumeSource.h"
#include "ui_ThresholdingPanel.h"
#include "ControlPanelBase.h"
#include "DisplayModel.h"
#include "PipelineModel.h"
#include "SurfaceSource.h"
#include "DisplayFramework.h"
#include "View3D.h"

#include "vtkPlane.h"
#include "vtkPointData.h"
#include "vtkCutter.h"
#include "vtkUnstructuredGrid.h"
#include "vtkClipPolyData.h"
#include "vtkClipVolume.h"
#include "vtkPlaneWidget.h"
#include "vtkProbeFilter.h"

#include "vtkImplicitPlaneWidget2.h"
#include "vtkImplicitPlaneWidget.h"
#include "vtkImplicitPlaneRepresentation.h"

#include "itkImage.h"
#include "itkImageDuplicator.h"
#include "itkThresholdImageFilter.h"
#include "itkImageRegionIterator.h"

class ThresholdingPanel : public ControlPanelBase
{
	Q_OBJECT
public:
	ThresholdingPanel(QWidget *parent) : ControlPanelBase(parent)
	{
		m_ui.setupUi(this);
		m_panelType = ptThresholding;
		m_planeWidget=NULL;

		connect(m_ui.btn_apply, SIGNAL(clicked()), SLOT(slotApply()));
		connect(m_ui.btn_cutApply, SIGNAL(clicked()), SLOT(slotCuttingApply()));
		connect(m_ui.btn_cutPanel, SIGNAL(toggled(bool)), SLOT(setupCuttingPanel(bool)));
		connect(m_ui.btn_ggo, SIGNAL(clicked()), SLOT(slotGGO()));
	}

	~ThresholdingPanel()
	{
		if ( m_planeWidget)
		{
			m_planeWidget->Off();
			m_planeWidget->Delete();
			m_planeWidget=NULL;
		}
	}

	void setSource(const QModelIndex& index)
	{
		if ( m_isource.isValid())
		{
			m_ui.slider_lower->disconnect(this, SLOT(slotThresholding()));
			m_ui.slider_upper->disconnect(this, SLOT(slotThresholding()));
		}

		ControlPanelBase::setSource(index);
		Source *source = currentSource();
		if ( source && source->type() == stVolume)
		{
			VolumeSourceBase *vsource = (VolumeSourceBase *)source;

			double range[2];
			vsource->data()->GetScalarRange(range);

			m_ui.slider_lower->setMinimum(range[0]);
			m_ui.slider_lower->setMaximum(range[1]);
			m_ui.slider_lower->setValue(vsource->getLowerThreshold());
			m_ui.slider_upper->setMinimum(range[0]);
			m_ui.slider_upper->setMaximum(range[1]);
			m_ui.slider_upper->setValue(vsource->getUpperThreshold());

			connect(m_ui.slider_lower, SIGNAL(valueChanged(int)), SLOT(slotThresholding()));
			connect(m_ui.slider_upper, SIGNAL(valueChanged(int)), SLOT(slotThresholding()));
		}
	}
	
private slots:
	void slotApply()
	{
		if ( !currentSource())
			return;

		switch(currentSource()->dataType())
		{
		case VTK_UNSIGNED_CHAR:
			thresholding<unsigned char>();
			break;
		case VTK_SHORT:
			thresholding<short>();
			break;
		case VTK_INT:
			thresholding<int>();
			break;
		case VTK_FLOAT:
			thresholding<float>();
			break;
		default:
			break;
		}

	}

	virtual void slotThresholding()
	{
		Source *source = currentSource();
		if ( source->type()==stVolume)
			((VolumeSourceBase*)source)->setThresholdBetween(m_ui.slider_lower->value(), m_ui.slider_upper->value());
	}

	void setupCuttingPanel(bool checked)
	{
		if ( m_planeWidget)
		{
			m_planeWidget->Off();
			m_planeWidget->Delete();
			m_planeWidget=NULL;
		}

		Source *source = currentSource();

		if ( !source)
		{
			m_ui.btn_cutPanel->setChecked(false);
			return;
		}

		QModelIndex index = m_pipelineModel->indexFromSource(source);
		if ( !index.isValid())
		{
			m_ui.btn_cutPanel->setChecked(false);
			return;
		}

		if (!m_displayModel->isDisplayingModel(index))
		{
			m_ui.btn_cutPanel->setChecked(false);
			return;
		}

		if ( checked)
		{
			m_planeWidget = vtkPlaneWidget::New();
			m_planeWidget->SetInteractor(m_displayModel->activeInteractor());

			if ( source->type()==stVolume)
			{
				m_planeWidget->SetInput((source)->data());
			}
			else if ( source->type() == stSurface)
			{
				m_planeWidget->SetInput(((SurfaceSource*)source)->sdata());
			}
			m_planeWidget->NormalToXAxisOn();
			m_planeWidget->SetResolution(50);
			m_planeWidget->SetRepresentationToSurface();
			m_planeWidget->PlaceWidget();
			m_planeWidget->On();
		}
	}




	virtual void slotCuttingApply()
	{
		if ( !m_ui.btn_cutPanel->isChecked())
			return;

		Source *source = currentSource();

		vtkPlane *plane = vtkPlane::New();
		m_planeWidget->GetPlane(plane);

		if ( source->type()==stVolume)
		{
			switch(source->dataType())
			{
			case VTK_UNSIGNED_CHAR:
				cutting<unsigned char>(plane);
				break;
			case VTK_SHORT:
				cutting<short>(plane);
				break;
			case VTK_INT:
				cutting<int>(plane);
				break;
			case VTK_FLOAT:
				cutting<float>(plane);
				break;
			default:
				break;
			}
		}
		else if ( source->type() == stSurface)
		{
			vtkClipPolyData *clipper = vtkClipPolyData::New();

			clipper->SetInput(((SurfaceSource*)source)->sdata());
			clipper->SetClipFunction(plane);
			clipper->GenerateClippedOutputOn();
			clipper->SetValue(0.5);
			clipper->Update();

			SurfaceSource* surface1 = new SurfaceSource("Surface1");
			surface1->setData(clipper->GetClippedOutput());

			SurfaceSource* surface2 = new SurfaceSource("Surface2");
			surface2->setData(clipper->GetOutput());

			source->addChild(surface1);
			source->addChild(surface2);
		}
	}



	void slotGGO()
	{
		//automatic GGO detection

		Source *source = currentSource();

		if (source->type()!=stVolume)
		{
			qDebug("Source type is not volume");
			return;
		}

		switch(source->dataType())
		{
		case VTK_UNSIGNED_CHAR:
			autoGGOSegmentation<unsigned char>();
			break;
		case VTK_SHORT:
			autoGGOSegmentation<short>();
			break;
		case VTK_INT:
			autoGGOSegmentation<int>();
			break;
		case VTK_FLOAT:
			autoGGOSegmentation<float>();
			break;
		default:
			break;
		}
	}


private:
	template<class FPixelType>
	void thresholding()
	{
		typedef itk::Image<FPixelType, 3> FImageType;
		typedef itk::ThresholdImageFilter<FImageType> ThresholdImageFilter;

		typename ThresholdImageFilter::Pointer thresholder = ThresholdImageFilter::New();

		typename FImageType::Pointer volume = ((VolumeSource<FImageType>*)currentSource())->getVolume();

		thresholder->SetInput(volume);
		thresholder->ThresholdOutside(m_ui.slider_lower->value(), m_ui.slider_upper->value());
		thresholder->SetOutsideValue(m_ui.slider_lower->value());
		thresholder->Update();

		VolumeSource<FImageType> *targetSource = 
			new VolumeSource<FImageType>(QString("thresholded#%1").arg(currentSource()->rowCount()+1));
		targetSource->setVolume(thresholder->GetOutput());

		currentSource()->addChild(targetSource);
	}

	template<class FPixelType>
	void cutting(vtkPlane *plane)
	{
		typedef itk::Image<FPixelType, 3> FImageType;
		typedef itk::ImageDuplicator<FImageType> ImageDuplicator;
		typedef itk::ImageRegionIterator<FImageType> ImageRegionIterator;

		Source *source = currentSource();

		// create two image
		typename FImageType::Pointer origVolume = ((VolumeSource<FImageType>*)source)->getVolume();

		typename ImageDuplicator::Pointer dup1 = ImageDuplicator::New();
		typename ImageDuplicator::Pointer dup2 = ImageDuplicator::New();
		dup1->SetInputImage(origVolume);
		dup2->SetInputImage(origVolume);
		dup1->Update();
		dup2->Update();

		double orig[3];
		double norm[3];

		plane->GetOrigin(orig);
		plane->GetNormal(norm);

		double d = - (norm[0]*orig[0]+norm[1]*orig[1]+norm[2]*orig[2]);

		printf("Orig:%f,%f,%f\n", orig[0], orig[1], orig[2]);
		printf("Norm:%f,%f,%f\n", norm[0], norm[1], norm[2]);

		double *point;
		double dist;
		vtkImageData *vtkData = source->data();

		typename FImageType::Pointer volume1 = dup1->GetOutput();
		typename FImageType::Pointer volume2 = dup2->GetOutput();

		typename FImageType::PixelType *buf  = origVolume->GetBufferPointer();
		typename FImageType::PixelType *buf1 = volume1->GetBufferPointer();
		typename FImageType::PixelType *buf2 = volume2->GetBufferPointer();

		for ( int i = 0; i < vtkData->GetNumberOfPoints(); i++)
		{
			point = vtkData->GetPoint(i);
			dist = norm[0]*point[0]+norm[1]*point[1]+norm[2]*point[2] + d;

			if ( dist > 0)
			{
				buf1[i] = 0;
				buf2[i] = buf[i];
			}
			else
			{
				buf1[i] = buf[i];
				buf2[i] = 0;
			}
		}


		VolumeSource<FImageType> *source1 = new VolumeSource<FImageType>(QString("Volume1"));
		source1->setVolume(dup1->GetOutput());

		VolumeSource<FImageType> *source2 = new VolumeSource<FImageType>(QString("Volume2"));
		source2->setVolume(dup2->GetOutput());

		currentSource()->addChild(source1);
		currentSource()->addChild(source2);
	}



	template<class FPixelType>
	void autoGGOSegmentation()
	{
        /*
		typedef itk::Image<FPixelType, 3> FImageType;
		typedef itk::Image<unsigned char, 3> FOImageType;
		typedef itk::AutoGGOSegmentationFilter<FImageType, FOImageType> AutoGGOSegmentationFilterType;


		Source *source = currentSource();
		typename FImageType::Pointer origVolume = ((VolumeSource<FImageType>*)source)->getVolume();


		typename AutoGGOSegmentationFilterType::Pointer filter = AutoGGOSegmentationFilterType::New();
		filter->SetInput(origVolume);
		filter->Update();

		VolumeSource<FOImageType> *smoothed = new VolumeSource<FOImageType>(QString("Smoothed"));
		smoothed->setVolume(filter->GetSmoothedImage());
		currentSource()->addChild(smoothed);

		VolumeSource<FOImageType> *otsu = new VolumeSource<FOImageType>(QString("Otsu"));
		otsu->setVolume(filter->GetFirstOtsuImage());
		smoothed->addChild(otsu);

		VolumeSource<FOImageType> *airRemoved = new VolumeSource<FOImageType>(QString("AirRemoved"));
		airRemoved->setVolume(filter->GetAirRemovedImage());
		otsu->addChild(airRemoved);

		VolumeSource<FOImageType> *objectsRemoved = new VolumeSource<FOImageType>(QString("ObjectsRemoved"));
		objectsRemoved->setVolume(filter->GetObjectsRemovedImage());
		airRemoved->addChild(objectsRemoved);

		VolumeSource<FOImageType> *autoLung = new VolumeSource<FOImageType>(QString("Lung"));
		autoLung->setVolume(filter->GetLungImage());
		currentSource()->addChild(autoLung);

		VolumeSource<FOImageType> *lungVessels = new VolumeSource<FOImageType>(QString("Lung Vessels"));
		lungVessels->setVolume(filter->GetVesselImage());
		autoLung->addChild(lungVessels);

		VolumeSource<FImageType> *bgRemoved = new VolumeSource<FImageType>(QString("BGRemoved"));
		bgRemoved->setVolume(filter->GetLungWithoutBGImage());
		autoLung->addChild(bgRemoved);

		VolumeSource<FOImageType> *otsu2 = new VolumeSource<FOImageType>(QString("Otsu2"));
		otsu2->setVolume(filter->GetSecondOtsuImage());
		bgRemoved->addChild(otsu2);

		VolumeSource<FImageType> *weighted = new VolumeSource<FImageType>(QString("Weighted"));
		weighted->setVolume(filter->GetWeightedImage());
		otsu2->addChild(weighted);

		VolumeSource<FOImageType> *autoGGO = new VolumeSource<FOImageType>(QString("AutoGGO"));
		autoGGO->setVolume(filter->GetOutput());
		autoLung->addChild(autoGGO);*/

	}

private:
	Ui::ThresholdingPanel m_ui;
	vtkPlaneWidget *m_planeWidget;
};


#endif


